MODULE_NAME='Via - VHM Status and Call Home'
(
    Dev dvVHMClient,
    Dev vdvVHM
)

(***********************************************************)
(***********************************************************)
(*  FILE_LAST_MODIFIED_ON: 04/04/2006  AT: 11:33:16        *)
(***********************************************************)
(* System Type : NetLinx                                   *)
(***********************************************************)
(* REV HISTORY:                                            *)
(***********************************************************)
(*
    $History: $
*)    
#DEFINE DEBUG = 1
(***********************************************************)
(*               CONSTANT DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_CONSTANT
cnMaxDebugLen = 90

cnDailyUpdateTimelineID = 1002
cnMaxQueueSize = 30

(***********************************************************)
(*              DATA TYPE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_TYPE
Structure _sQueue
{
    Integer nItems
    Char strData[cnMaxQueueSize][200]
}
(***********************************************************)
(*               VARIABLE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_VARIABLE
Volatile _sQueue sQueue

Volatile Char strClientBuffer[2000]
Volatile Char strJunk[5]
VOlatile Integer nOnline
Volatile Char strClientKey[6]
Volatile Long lDailyUpdateTimes[] = {86400000}

(***********************************************************)
(*        SUBROUTINE/FUNCTION DEFINITIONS GO BELOW         *)
(***********************************************************)
(* EXAMPLE: DEFINE_FUNCTION <RETURN_TYPE> <NAME> (<PARAMETERS>) *)
(* EXAMPLE: DEFINE_CALL '<NAME>' (<PARAMETERS>) *)
Define_Call 'DEBUG_OUTPUT' (Integer nLine, Char strData[])
{
    #IF_DEFINED DEBUG
    Char strTempData[200]
    Integer nLoop
    If (Length_String(strData) <= cnMaxDebugLen)
    {
	Send_String 0,"__FILE__,' Line:',Itoa(nLine),' ',strData"
    }
    Else
    {
	nLoop = 0
	While(Length_String(strData) > cnMaxDebugLen)
	{
	    nLoop++
	    strTempData = Left_String(strData,cnMaxDebugLen)
	    strData = Right_String(strData,Length_String(strData)-cnMaxDebugLen)
	    If (nLoop == 1)
		Send_String 0,"__FILE__,' Line:',Itoa(nLine),' ',strTempData"
	    Else
		Send_String 0,"'Continued: ',strTempData"
	}
	Send_String 0,"'Continued: ',strData"
    }
    #END_IF
}

Define_Call 'ADD_TO_QUEUE' (Char strData[])
{    
    If (sQueue.nItems >= cnMaxQueueSize)
    {
	Call 'DEBUG_OUTPUT' (__LINE__,"'ADD_TO_QUEUE: Queue Full.  Dumping all previous items.'")
	sQueue.nItems = 1
    }
    Else
    {
	sQueue.nItems++
    }
    Call 'DEBUG_OUTPUT' (__LINE__,"'ADD_TO_QUEUE: sQueue.nItems=',Itoa(sQueue.nItems)")
    sQueue.strData[sQueue.nItems] = strData
    If(nOnline = 0)
    {
	IP_Client_Open(dvVHMClient.Port,'myviahome.com', 80, IP_TCP)
    }
    Else
    {
	Call 'DEBUG_OUTPUT' (__LINE__,"'ADD_TO_QUEUE: Already connected'")
    }
}

Define_Function Char[255] fnURLEncode(Char szText[])
{
    Local_Var Integer nI, nByte
    Char szChar[3], szEncodedText[256]
    For(nI=1; nI <= Length_String(szText); nI++)
    {
	Switch(szText[nI])		
	{			/* Reserved characters */
	    case 36: szChar = '%24' // Dollar  "$"
	    case 38: szChar = '%26' // Ampersand  "&"
	    case 43: szChar = '%2B' // Plus  "+"			
	    case 44: szChar = '%2C' // Comma  ","			
	    case 47: szChar = '%2F' // Forward slash/Virgule  "/"			
	    case 58: szChar = '%3A' // Colon  ":"			
	    case 59: szChar = '%3B' // Semi-colon  ";"			
	    case 61: szChar = '%3D' // Equals  "="			
	    case 63: szChar = '%3F' // Question mark  "?"			
	    case 64: szChar = '%40' // 'At' symbol  "@"			
	    /* Unsafe characters */			
	    case 32:  szChar = '%20' // Space			
	    case 34:  szChar = '%22' // Quotation marks			
	    case 60:  szChar = '%3C' // 'Less Than' symbol  "<"			
	    case 62:  szChar = '%3E' // 'Greater Than' symbol  ">"			
	    case 35:  szChar = '%23' // 'Pound' character  "#"			
	    case 37:  szChar = '%25' // Percent character  '%"			
	    case 123: szChar = '%7B' // Left Curly Brace  "{"			
	    case 125: szChar = '%7D' // Right Curly Brace  "}"			
	    case 124: szChar = '%7C' // Vertical Bar/Pipe  "|"			
	    case 92:  szChar = '%5C' // Backslash  "\"			
	    case 94:  szChar = '%5E' // Caret  "^"			
	    case 126: szChar = '%7E' // Tilde  "~"			
	    case 91:  szChar = '%5B' // Left Square Bracket  "["			
	    case 93:  szChar = '%5D' // Right Square Bracket  "]"			
	    case 96:  szChar = '%60' // Grave Accent  "`"			
	    default: szChar = Mid_String(szText,nI,1)
	}
	szEncodedText = "szEncodedText,szChar"
    }
    return(szEncodedText)
}

Define_Function CHAR[100] GET_IP_ERROR (LONG lERR)
{
    Switch (lERR)
    {
	Case 0:
	    Return "";
	Case 2:
	    Return "'IP ERROR (',ITOA(lERR),'): General Failure (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Case 4:
	    Return "'IP ERROR (',ITOA(lERR),'): unknown host (IP_CLIENT_OPEN)'";
	Case 6:
	    Return "'IP ERROR (',ITOA(lERR),'): connection refused (IP_CLIENT_OPEN)'";
	Case 7:
	    Return "'IP ERROR (',ITOA(lERR),'): connection timed out (IP_CLIENT_OPEN)'";
	Case 8:
	    Return "'IP ERROR (',ITOA(lERR),'): unknown connection error (IP_CLIENT_OPEN)'";
	Case 9:
	    Return "'IP ERROR (',ITOA(lERR),'): Already closed (IP_CLIENT_CLOSE/IP_SERVER_CLOSE)'";
	Case 10:
	    Return "'IP ERROR (',ITOA(lERR),'): Binding error (IP_SERVER_OPEN)'";
	Case 11:
	    Return "'IP ERROR (',ITOA(lERR),'): Listening error (IP_SERVER_OPEN)'";
	Case 14:
	    Return "'IP ERROR (',ITOA(lERR),'): local port already used (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Case 15:
	    Return "'IP ERROR (',ITOA(lERR),'): UDP socket already listening (IP_SERVER_OPEN)'";
	Case 16:
	    Return "'IP ERROR (',ITOA(lERR),'): too many open sockets (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Default:
	    Return "'IP ERROR (',ITOA(lERR),'): Unknown'";
    }
}

(***********************************************************)
(*                STARTUP CODE GOES BELOW                  *)
(***********************************************************)
DEFINE_START

(***********************************************************)
(*                THE EVENTS GO BELOW                      *)
(***********************************************************)
DEFINE_EVENT

Data_Event[dvVHMClient]
{
    Online:
    {
	Local_Var Char strTemp[2000]
	Local_Var Integer nLen, nI
	
	nOnline = 1
	nLen = Length_String(strClientKey) + Length_String(sQueue.strData[1]) + 5
	Send_String dvVHMClient,"'POST /api/report HTTP/1.1',13,10,'Host:myviahome.com',13,10,'Content-Length: ',Itoa(nLen),13,10,'Content-Type: application/x-www-form-urlencoded',13,10,13,10,'key=',strClientKey,'&',sQueue.strData[1],13,10"
	Call 'DEBUG_OUTPUT'(__LINE__,"'POST /api/report HTTP/1.1',13,10,'Host:myviahome.com',13,10,'Content-Length: ',Itoa(nLen),13,10,'Content-Type: application/x-www-form-urlencoded',13,10,13,10,'key=',strClientKey,'&',sQueue.strData[1],13,10")
	sQueue.nItems--
	For (nI = 1; nI < sQueue.nItems; nI++) // Remove queue item
	{
	    sQueue.strData[nI] = sQueue.strData[nI+1]
	}
	Wait 20
	    IP_Client_Close(dvVHMClient.Port)
    }
    Offline:
    {
	nOnline = 0
	If (sQueue.nItems > 0)
	{
	    Wait 20
		IP_Client_Open(dvVHMClient.Port,'myviahome.com', 80, IP_TCP)
	}
    }
    OnError:
    {
	strClientBuffer = ''
	Call 'DEBUG_OUTPUT' (__LINE__, "GET_IP_ERROR(Data.Number)");
	nOnline = 0
    }
    String:
    {
	Call 'DEBUG_OUTPUT' (__LINE__,"'dvVHMClient In:',Data.Text")
    }
}

Data_Event[vdvVHM]
{
    Command:
    {
	Local_Var Char strCommand[20], strAlertName[30], strAlertDetails[100]
	Local_Var Integer nI, nAlertType
	// Format: SERVERPORT=nServerPort
	Call 'DEBUG_OUTPUT' (__LINE__,"'vdvVHM Command Data.Text=',Data.Text");
	If (Find_String(Data.Text,'=',1))
	{
	    strCommand = Upper_String(Remove_String(Data.Text,'=',1))
	    If (strCommand == 'UPDATETIME=')
	    {
		lDailyUpdateTimes[1] = Atoi(Data.Text) * 1000
		Timeline_Create (cnDailyUpdateTimelineID,lDailyUpdateTimes,1,Timeline_Absolute,Timeline_Repeat)
	    }
	    Else If (strCommand == 'CLIENTKEY=')
	    {
		strClientKey = Data.Text
	    }
	    Else If (Find_String(strCommand,'ALERT:',1))
	    {
		strJunk = Remove_String(strCommand,'ALERT=',1)
		If (Find_String(strCommand,'TYPE(',1))
		{
		    strJunk = Remove_String(strCommand,'NAME(',1)
		    nAlertType = Atoi(Remove_String(strCommand,')',1))
		}
		If (Find_String(strCommand,'NAME(',1))
		{
		    strJunk = Remove_String(strCommand,'NAME(',1)
		    strAlertName = Remove_String(strCommand,')',1)
		}
		If (Find_String(strCommand,'DETAILS(',1))
		{
		    strJunk = Remove_String(strCommand,'DETAILS(',1)
		    strAlertDetails = Remove_String(strCommand,')',1)
		}
		Call 'ADD_TO_QUEUE' ("'status=0&alertType=',Itoa(nAlertType),'&alertName',fnURLEncode(strAlertName),'&alertDetails=',fnURLEncode(strAlertDetails)")
	    }
	}
    }
}

Level_Event[vdvVHM,1]
{
    Call 'DEBUG_OUTPUT' (__LINE__,"'status = ',Itoa(Level.Value)")
    Call 'ADD_TO_QUEUE' ("'status=',Itoa(Level.Value)")
}

Timeline_Event[cnDailyUpdateTimelineID]
{
    Call 'DEBUG_OUTPUT' (__LINE__,"'Timeline_Event[cnDailyUpdateTimelineID]'")
    Call 'ADD_TO_QUEUE' ('status=0')
}

(***********************************************************)
(*            THE ACTUAL PROGRAM GOES BELOW                *)
(***********************************************************)
DEFINE_PROGRAM

(***********************************************************)
(*                     END OF PROGRAM                      *)
(*        DO NOT PUT ANY CODE BELOW THIS COMMENT           *)
(***********************************************************)